package com.atguigu.gmall.item.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gamll.feign.product.ProductSkuDetailFeignClient;
import com.atguigu.gamll.feign.search.SearchFeignClient;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.item.service.SkuDetailService;
import com.atguigu.gmall.product.entity.SkuImage;
import com.atguigu.gmall.product.entity.SkuInfo;
import com.atguigu.gmall.product.entity.SpuSaleAttr;
import com.atguigu.gmall.product.vo.CategoryViewDTO;
import com.atguigu.gmall.product.vo.SkuDetailVo;
import com.atguigu.gmall.starter.cache.aspect.annotation.MallCache;
import com.atguigu.gmall.starter.cache.service.CacheService;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.threads.ThreadPoolExecutor;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author lhh
 * @ClassName SkuDetailRpcServiceImpl
 * @date 2022 2022/12/3
 */
@Slf4j
@Service
public class SkuDetailServiceImpl implements SkuDetailService {

    @Autowired
    ProductSkuDetailFeignClient skuDetailFeignClient;

    @Resource
    ThreadPoolExecutor poolExecutor;

    @Resource
    CacheService cacheService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    RedissonClient redissonClient;

    @Resource
    SearchFeignClient searchFeignClient;

    @Override
    @MallCache(cacheKey = RedisConst.SKU_DETAIL_CACHE+"#{#args[0]}",
               bitMapName = RedisConst.SKUID_BITMAP,
               bitMapKey = "#{#args[0]}",
               lockKey=RedisConst.SKU_LOCK+"#{#args[0]}",
               ttl=7,
               unit=TimeUnit.DAYS)
    public SkuDetailVo getSkuDetailData(Long skuId) {
       return getDataFromRpc(skuId);
    }

    @Override
    public void incrHostScore(Long skuId) {
        //远程调用es增加热度
        CompletableFuture.runAsync(() -> {
            Long increment = redisTemplate.opsForValue().increment(RedisConst.SKU_HOST_SCORE + skuId);
            if(increment%100==0){
                //同步es
                searchFeignClient.updateHotScore(skuId,increment);
            }
        },poolExecutor);
    }



    /*public SkuDetailVo getSkuDetailDataFrom(Long skuId) throws InterruptedException {
        SkuDetailVo returnVal = null;
        //1.先查缓存
        returnVal = cacheService.getFromCache(skuId);
        //判断缓存是否为空
        //2.不为空直接返回
        if (returnVal != null) {
            return returnVal;
        }
        //3.判断位图
        boolean contain = cacheService.mightContain(skuId);
        if(!contain){
            //4.位图中未命中 则直接return null
            return  null;
        }
        //5.位图命中，准备回源，先加锁，防止缓存击穿
        RLock lock = redissonClient.getLock(RedisConst.SKU_LOCK + skuId);
        boolean tryLock=false;
        try {
            //6.加锁
            tryLock= lock.tryLock();
            //7.如果抢到锁，查询数据库，并存入缓存
            if(tryLock){
                returnVal = this.getDataFromRpc(skuId);
                //8.存入缓存中
                cacheService.saveData(skuId,returnVal);
                return returnVal;
            }else {
                //9.抢锁失败  等待500ms 准备查询缓存
                TimeUnit.MILLISECONDS.sleep(500);
                return cacheService.getFromCache(skuId);
            }
        }finally {
            if(tryLock){
                try {
                    //10.释放锁
                    lock.unlock();
                }catch (Exception e){
                }
            }
        }
    }*/

    /**
     * 本地缓存
     */
    //ConcurrentHashMap<Long, SkuDetailVo> result = new ConcurrentHashMap();
    /*public SkuDetailVo getSkuDetailDataWithLocalLock(Long skuId) {

        SkuDetailVo returnVal = null;
        //1.先查缓存
        log.info("开始查询缓存");
        returnVal = cacheService.getFromCache(skuId);
        if (returnVal == null) {
            //2.位图中是否存在
            boolean contain = cacheService.mightContain(skuId);
            if (!contain) {
                log.info("bitMap没有，疑似攻击请求，直接打回");
                return null;
            }
            //3.缓存中没有,回源
            log.info("bitMap有,准备回源，需要加锁防止击穿");
            //准备拦截缓存击穿问题 利用锁机制来实现
            ReentrantLock lock = new ReentrantLock();
            boolean tryLock = lock.tryLock();
            if (tryLock) {
                log.info("加锁成功。。。正在回源");
                //抢锁成功
                try {
                    returnVal = getDataFromRpc(skuId);
                    //4保存到缓存
                    cacheService.saveData(skuId, returnVal);
                } finally {
                    lock.unlock();
                }
            } else {
                //抢锁失败 500毫秒后直接看缓存
                log.info("加锁失败。。。等待查询缓存");
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                    returnVal = cacheService.getFromCache(skuId);
                } catch (InterruptedException e) {
                }
            }
        }
        return returnVal;
    }*/

    public SkuDetailVo getSkuDetailDataNull(Long skuId) {

        SkuDetailVo detailVo = new SkuDetailVo();

        String result = redisTemplate.opsForValue().get(skuId);
        if (result == null) {
            log.info("缓存没有，进行会员");
            detailVo = getDataFromRpc(skuId);
            String toJSONString = JSON.toJSONString(detailVo);
            redisTemplate.opsForValue().set(skuId.toString(), toJSONString);
            return detailVo;
        }
        detailVo = JSON.parseObject(result, SkuDetailVo.class);
        return detailVo;
    }

    private SkuDetailVo getDataFromRpc(Long skuId) {
        SkuDetailVo skuDetailVo = new SkuDetailVo();
        //获取sku-info与 imageList
        CompletableFuture<SkuInfo> skuInfoFuture = CompletableFuture.supplyAsync(() -> {
            SkuInfo skuInfo = skuDetailFeignClient.getSkuInfo(skuId).getData();
            return skuInfo;
        }, poolExecutor);

        CompletableFuture<Void> skuImageFuture = skuInfoFuture.thenAcceptAsync(res -> {
            List<SkuImage> skuImages = skuDetailFeignClient.getSkuImages(skuId).getData();
            res.setSkuImageList(skuImages);
            skuDetailVo.setSkuInfo(res);
        }, poolExecutor);

        //分类视图
        CompletableFuture<Void> categoryFuture = skuInfoFuture.thenAcceptAsync(res -> {
            CategoryViewDTO categoryViewDTO = skuDetailFeignClient.getCategoryTreeWithC3Id(res.getCategory3Id()).getData();
            skuDetailVo.setCategoryView(categoryViewDTO);
        }, poolExecutor);

        //获取 实时价格
        CompletableFuture<Void> priceFuture = CompletableFuture.runAsync(() -> {
            BigDecimal price = skuDetailFeignClient.getPrice(skuId).getData();
            skuDetailVo.setPrice(price);
        }, poolExecutor);

        //获取 spu销售属性名和值 集合
        CompletableFuture<Void> spuSaleAttrFuture = skuInfoFuture.thenAcceptAsync(res -> {
            List<SpuSaleAttr> spuSaleAttrs = skuDetailFeignClient.getSpuSaleAttr(res.getSpuId(), skuId).getData();
            skuDetailVo.setSpuSaleAttrList(spuSaleAttrs);
        }, poolExecutor);

        //获取 valuesSkuJson
        CompletableFuture<Void> valueJsonFuture = skuInfoFuture.thenAcceptAsync(res -> {
            String valueJson = skuDetailFeignClient.getValuesSkuJson(res.getSpuId()).getData();
            skuDetailVo.setValuesSkuJson(valueJson);
        }, poolExecutor);

        CompletableFuture.allOf(skuInfoFuture, categoryFuture, skuImageFuture,
                        priceFuture, spuSaleAttrFuture, valueJsonFuture)
                .join();
        return skuDetailVo;
    }
}
